package com.wyfx.blockchain.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.wyfx.blockchain.common.Constants;
import com.wyfx.blockchain.common.response.ResultResponse;
import com.wyfx.blockchain.dao.RocksDao;
import com.wyfx.blockchain.entity.Block;
import com.wyfx.blockchain.service.IBlockService;
import com.wyfx.blockchain.service.ISyncService;
import com.wyfx.blockchain.utils.CommonUtils;
import com.wyfx.blockchain.utils.FastJsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Objects;


/**
 * @Description
 * @Auther: wsm
 * @Date: 2020/9/7 10:10
 */
@Service
public class ISyncServiceImpl implements ISyncService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private IBlockService iBlockService;

    @Autowired
    private RocksDao rocksDao;

    /**
     * @param masterIp
     * @param masterPort
     * @desc 同步主节点区块链数据到本地区块链
     * @Return boolean
     * @Author wsm
     * @date 2020/9/9 9:58
     */
    @Override
    public boolean syncBlockChainData(String masterIp, String masterPort) {
        //获取本节点的最后一个区块
        Block lastBlock = iBlockService.getLastBlock();
        //  如果本地不存在区块 则同步整个主节点的区块链
        if (lastBlock == null) {
            SyncAllBlockChain(masterIp, masterPort);
        }

        //获取主节点的最后一个区块
        String generateUrl = CommonUtils.generateUrl(masterIp, Integer.parseInt(masterPort), "block/last");
        ResultResponse resp = restTemplate.getForObject(generateUrl, ResultResponse.class);
        Block masterLastBlock = parseResultResponse(resp);

        //如果不相等则循环同步
        while (!Objects.equals(lastBlock.getHash(), masterLastBlock.getHash())) {
            // 通过本地节点的最后一个区块hash去主节点查询该区块的下一个区块
            String url = CommonUtils.generateUrl(masterIp, Integer.parseInt(masterPort), "block/next/{hash}");
            ResultResponse response = restTemplate.getForObject(url, ResultResponse.class, lastBlock.getHash());
            Block nextBlock = parseResultResponse(response);
            //保存下一区块到本地区块链并维护 区块之间的关系
            //保存上一区块对该区块的key value映射  当需要查询下一区块的hash时通过key= key_next_+当前区块hash 查询
            rocksDao.save(Constants.KEY_BLOCK_NEXT_PREFIX + nextBlock.getPreviousHash(), nextBlock.getHash());
            //存入rocksDB  维护hash 与区块数据的关系 通过hash可以查询到区块的信息
            rocksDao.save(nextBlock.getHash(), FastJsonUtil.toJSONString(nextBlock));
            //设置最后一个block的key value
            rocksDao.save(Constants.KEY_OF_LAST_BLOCK, nextBlock.getHash());
            //将同步的区块设置为本地的最后一个区块
            lastBlock = nextBlock;
        }
        return true;
    }

    /**
     * @param masterIp
     * @param masterPort
     * @desc 同步整个主节点的区块链
     * @Return void
     * @Author wsm
     * @date 2020/9/9 10:55
     */
    private void SyncAllBlockChain(String masterIp, String masterPort) {
        String url = CommonUtils.generateUrl(masterIp, Integer.parseInt(masterPort), "block/first");
        ResultResponse response = restTemplate.getForObject(url, ResultResponse.class);
        //本地创世块
        Block firstBlock = parseResultResponse(response);
        rocksDao.save(firstBlock.getHash(), FastJsonUtil.toJSONString(firstBlock));
        //设置第一个block的key value 保存数据到rocksDB 创世块生成时即是第一块也是最后一块
        rocksDao.save(Constants.KEY_OF_FIRST_BLOCK, firstBlock.getHash());
        //设置最后一个block的key value  每次新增区块时维护一下最后一个区块的映射关系
        rocksDao.save(Constants.KEY_OF_LAST_BLOCK, firstBlock.getHash());

        //同步其他区块
        String nexturl = CommonUtils.generateUrl(masterIp, Integer.parseInt(masterPort), "block/next/{hash}");
        ResultResponse resp = restTemplate.getForObject(nexturl, ResultResponse.class, firstBlock.getHash());
        Block nextBlock = parseResultResponse(resp);
        //保存下一区块到本地区块链并维护 区块直接的关系
        //保存上一区块对该区块的key value映射  当需要查询下一区块的hash时通过key= key_next_+当前区块hash 查询
        rocksDao.save(Constants.KEY_BLOCK_NEXT_PREFIX + nextBlock.getPreviousHash(), nextBlock.getHash());
        //存入rocksDB  维护hash 与区块数据的关系 通过hash可以查询到区块的信息
        rocksDao.save(nextBlock.getHash(), FastJsonUtil.toJSONString(nextBlock));
        //设置最后一个block的key value
        rocksDao.save(Constants.KEY_OF_LAST_BLOCK, nextBlock);

    }

    /**
     * @param resultResponse
     * @desc 解析响应
     * @Return com.wyfx.blockchain.entity.Block
     * @Author wsm
     * @date 2020/9/10 15:14
     */
    private Block parseResultResponse(ResultResponse resultResponse) {
        String data = resultResponse.getData().toString();
        Block block = JSONObject.parseObject(data, Block.class);
        return block;
    }


}
